/**
* AutoSWF - Cody Cushing. March 26, 2009
* Copyright (c) 2009 Cody Cushing
* No use restrictions. Maintain this notice in all versions. 
**/

package com.autoswf.locator
{
	import com.autoswf.core.Link;
	import com.autoswf.core.Transport;
	
	import flash.errors.IllegalOperationError;
	import flash.utils.Dictionary;

	/**
	 * Locator class for providing resources to requesters. Uses handler in ResourceRequest to return 
	 * requested resource instance to client. If the requested resource does not exist the request is 
	 * bubbled up the chain. 
	 */
	public class Locator extends Link
	{
		/** Weak key dictionary to hold all resources. */
		protected var _resources:Dictionary;
		
		/** Strong key dictionary to prevent garbage collection on specific resources. */
		protected var _persistentResources:Dictionary;
		
		
		/**
		 * Constructor.
		 */		
		public function Locator(id:String)
		{
			super(id);
			
			_resources = new Dictionary(true);
			_persistentResources = new Dictionary(false);
		}
		
		
		/**
		 * This function responds to a ResourceRequest as the message object of a Transport, looks the resource up, 
		 * and passes it to the callback in the resource request. If the resource is not found, the transport is 
		 * passed up the chain.
		 * 
		 * @param param  A Transport Object containing a ResourceRequest. 
		 */		
		override public function sendUp(object:*):void
		{
			var transport:Transport = Transport(object);
			
			/* Something is requesting a resource */
			if(transport.message is ResourceRequest)
			{
				var request:ResourceRequest = ResourceRequest(transport.message);
				
				var resource:Object = _resources[request.resource];
				
				if(resource) /* resource exists at local tier, return it */
				{
					/* Point the request's result property to this resource. */
					request.result = resource;
					
					/* If there is a callback, pass the resource directly. */
					if(request.handler != null)
					{
						request.handler(resource);
					}
				}
				else /* resource may exist at a higher tier */
				{
					notifyParents(transport);
				}
				return
			}
			
			/* Something wants to register a resource */
			if(transport.message is ResourceRegistration)
			{
				var reg:ResourceRegistration = ResourceRegistration(transport.message);
				addResource(reg.resource, reg.registeredClass, reg.persistent);
			}
		}
		
		
		/**
		 * Makes a resource instance accessible by class definition key.
		 * 
		 * @param resource     The instance of the resource to be returned. 
		 * @param registerTo  The class or interface by which this resource can be requested.
		 */
		public function addResource(resource:Object, registerTo:Class, persistent:Boolean = false):void
		{
			registerTo(resource); /* throw coersion error on invalid registration */
			
			if(_resources[registerTo] != null)
			{
				throw new IllegalOperationError("Collision on resource registration key. This class functions as a multiton; only one resource per key.");
			}
			
			_resources[registerTo] = resource;
			
			if(persistent)
			{
				_persistentResources[resource] = registerTo;
			}
		}
		
		
		public function hasResource(resource:Class):Boolean
		{
			return (_resources[resource]);
		}
		
		
		public function removeResource(registeredClass:Class):void
		{
			if(_resources[registeredClass])
			{
				delete _resources[registeredClass];
			}
			
			if(_persistentResources[registeredClass])
			{
				delete _persistentResources[registeredClass];
			}
		}
	}
}

